
#ifndef FF_WRAPPERVK_H
#define FF_WRAPPERVK_H

#include <IUnityGraphics.h>
#include <IUnityGraphicsVulkan.h>

#include <platform/dynamic_library.h>

// only dynamic load is supported in plugins now
// #define FFVK_DECLARE_PROTOTYPES
#include <frame_flow_vk.h>

namespace FFSdkUnity::Vulkan {

enum FFUnityZSFormat {
    D32_SFLOAT_S8_UINT = 0,
    D24_UNORM_S8_UINT = 1,
    D16_UNORM_S8_UINT = 2
};

enum FFUnityColorFormat
{
    R8G8B8A8_UNORM = 0
};

struct FFUnityInputDescription {
    FFUnityColorFormat scene_fmt;
    FFUnityColorFormat output_fmt;
    FFUnityZSFormat zs_fmt;
    int width;
    int height;
};

struct FFUnityFrameData {
    FFStructureType        sType; // FF_STRUCTURE_TYPE_PER_FRAME_DATA`.
    const void*            pNext;
    FFPerFrameDataFlags    flags;
    void*                  sceneColor;
    void*                  depthStencil;
    FFMat4x4               viewProj;
    FFMat4x4               invViewProj;
};

struct FFUnityRemapTable {
    int remapTable[256];
};

class FFWrapperVK final {
public:
    FFWrapperVK() = default;

    FFWrapperVK(FFWrapperVK const &) = delete;
    FFWrapperVK &operator= (FFWrapperVK const &) = delete;

    FFWrapperVK(FFWrapperVK &&) = delete;
    FFWrapperVK &operator= (FFWrapperVK &&) = delete;

    ~FFWrapperVK() = default;

    [[nodiscard]] bool Init(IUnityGraphicsVulkan* unity_graphics) noexcept;
    [[nodiscard]] bool Destroy() noexcept;

    [[nodiscard]] FFResult OnActivate() const noexcept;
    [[nodiscard]] FFResult OnDeactivate() const noexcept;
    [[nodiscard]] FFBool OnIsActive() const noexcept;
    [[nodiscard]] FFResult OnSetPredictionAlgorithm(FFPredictionAlgorithm algorithm, bool extrapolation) const noexcept;
    [[nodiscard]] FFResult OnSetStencilRemapInfo(FFUnityRemapTable const *unity_table) noexcept;
    [[nodiscard]] FFResult OnProvidePerFrameData(FFUnityFrameData const *unity_data) noexcept;
    [[nodiscard]] FFResult OnSetInputDescription(FFUnityInputDescription const *unity_desc) noexcept;
    [[nodiscard]] FFResult OnDrawFrame(void* tex_handle) noexcept;

private:
    [[nodiscard]] bool IsInit() const noexcept;

    [[nodiscard]] bool LoadFrameFlow() noexcept;

    static void FFAPI_PTR SDKErrorCallback(char const *message, void *userData);

    bool RegisterImage(void* unity_handle, bool zs_image, const FFVKImageSync& initial_sync, FFVKImage& ff_image);
    void UnRegisterImage(FFVKImage& ff_image);

    // Define a list of API function pointers
#define FF_FUNCTION(func) PFN_##func func = nullptr;
#include <ff_function/ff_vk_functions.in>
#undef FF_FUNCTION

    FFVKInstance ffInstance_ = FF_NULL_HANDLE;
    FF::Platform::DynamicLibrary frameflowLib_{};
    // True when FF instance is provided by system via private API and must not be destroyed manually
    bool ffInstanceIsExternal_ = false;
    uint8_t m_frame_idx = 0;
    IUnityGraphicsVulkan* unity_graphics_ = nullptr;
};

} // namespace FFSdkUnity::Vulkan

#endif // FF_WRAPPERVK_H
